﻿using System;
using NHibernate;
using NHibernate.Criterion;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlClient;
using log4net;
using TeamCommunitySolution.Domain.DataLayer.Base;
using TeamCommunitySolution.Services.Factories;

namespace TeamCommunitySolution.Services.Repositories.Impl
{
    public class SystemUserRepository : ISystemUserRepository
    {
        /// <summary>
        /// The log instance.
        /// </summary>
        private static readonly ILog _log = LogManager.GetLogger(typeof(SystemUserRepository));

        /// <summary>
        /// The repository instance
        /// </summary>
        private static ISystemUserRepository _repo = null;

        /// <summary>
        /// Prevents a default instance of the <see cref="SystemUserRepository"/> class from being created.
        /// </summary>
        private SystemUserRepository()
        {
        }

        /// <summary>
        /// Gets the instance of the repository Instance.
        /// </summary>
        public static ISystemUserRepository Instance
        {
            get
            {
                if (_repo == null)
                {
                    _repo = new SystemUserRepository();
                }

                return _repo;
            }
        }

        #region ISystemUserRepository Members

        /// <summary>
        /// Saves the user.
        /// </summary>
        /// <param name="user">The user instance.</param>
        public void SaveUser(SystemUser user)
        {
            using (ISession session = NHSessionFactory.Factory.OpenSession())
            {
                var scope = session.BeginTransaction();
                try
                {
                    session.SaveOrUpdateCopy(user);
                    scope.Commit();
                }
                catch (SqlException ex)
                {
                    scope.Rollback();
                    string message = "Unable to save object [SystemUser] in the datastore";
                    this.HandleException(message, ex);
                }
            }
        }

        /// <summary>
        /// Gets the user.
        /// </summary>
        /// <param name="user">The user instance.</param>
        /// <returns>System User instance.</returns>
        public SystemUser GetUser(SystemUser user)
        {
            using (ISession session = NHSessionFactory.Factory.OpenSession())
            {
                try
                {
                    return session.Get<SystemUser>(user.Id);
                }
                catch (SqlException ex)
                {
                    string message = "Unable to find object [SystemUser] in the datastore";
                    this.HandleException(message, ex);
                }
            }
            return null;
        }

        /// <summary>
        /// Gets the user by field.
        /// </summary>
        /// <param name="field">The field.</param>
        /// <param name="fieldValue">The field value.</param>
        /// <returns>System User Instance.</returns>
        public SystemUser GetUserByField(string field, string fieldValue)
        {
            using (ISession session = NHSessionFactory.Factory.OpenSession())
            {
                ICriteria crit = session.CreateCriteria(typeof(SystemUser));
                crit.Add(Expression.Eq(field, fieldValue));

                IList<SystemUser> results = crit.List<SystemUser>();

                if (results.Count > 1)
                {
                    throw new SystemUserRepositoryException(string.Format("Duplicate objects [SystemUser] found in the datastore. Field: {0}, Value: {1}", field, fieldValue));
                }

                if (results.Count == 0)
                {
                    return null;
                }

                return results[0];
            }
        }

        /// <summary>
        /// Gets the user.
        /// </summary>
        /// <param name="email">The email.</param>
        /// <param name="password">The password.</param>
        /// <returns>An instance of the system user.</returns>
        public SystemUser GetUserByEmailAndPassword(string email, string password)
        {
            using (ISession session = NHSessionFactory.Factory.OpenSession())
            {
                ICriteria crit = session.CreateCriteria(typeof(SystemUser));
                crit.Add(Expression.Eq("Email", email));
                crit.Add(Expression.Eq("Password", password));

                IList<SystemUser> results = crit.List<SystemUser>();

                if (results.Count > 1)
                {
                    throw new SystemUserRepositoryException("Duplicate objects [SystemUser] found in the datastore.");
                }

                if (results.Count == 0)
                {
                    return null;
                }

                return results[0];
            }
        }

        /// <summary>
        /// Saves the multiple users.
        /// </summary>
        /// <param name="systemUserList">The system user list.</param>
        public void SaveMultipleUsers(List<SystemUser> systemUserList)
        {
            using (ISession session = NHSessionFactory.Factory.OpenSession())
            {
                var scope = session.BeginTransaction();
                try
                {
                    for (int i = 0; i < systemUserList.Count; i++)
                    {
                        session.SaveOrUpdateCopy(systemUserList[i]);
                    }

                    scope.Commit();
                }
                catch (SqlException ex)
                {
                    scope.Rollback();
                    string message = "Unable to save object [SystemUser] in the datastore";
                    this.HandleException(message, ex);
                }
            }
        }

        /// <summary>
        /// Deletes the user.
        /// </summary>
        /// <param name="user">The user in the datastore.</param>
        public void DeleteUser(SystemUser user)
        {
            using (ISession session = NHSessionFactory.Factory.OpenSession())
            {
                var scope = session.BeginTransaction();
                try
                {
                    session.Delete(user);

                    scope.Commit();
                }
                catch (SqlException ex)
                {
                    scope.Rollback();
                    string message = "Unable to delete object [SystemUser] from the datastore";
                    this.HandleException(message, ex);
                }
            }
        }

        /// <summary>
        /// Lists all users.
        /// </summary>
        /// <returns>List of users in the system.</returns>
        public IList<SystemUser> ListAllUsers()
        {
            using (ISession session = NHSessionFactory.Factory.OpenSession())
            {
                ICriteria crit = session.CreateCriteria(typeof(SystemUser));
                crit.AddOrder(Order.Asc("Email"));
                return crit.List<SystemUser>();
            }
        }
        #endregion

        /// <summary>
        /// Handles the exception.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="ex">The exception.</param>
        private void HandleException(string message, Exception ex)
        {
            _log.Error(message, ex);
            throw new SystemUserRepositoryException(message, ex);
        }
    }
}
